home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
ab20
/
ab20_archive
/
datacomm
/
xpr
/
xprzmodem-2.51.lzh
/
xprzmodem2.5
/
Receive.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-18
|
19KB
|
626 lines
/**********************************************************************
* Receive.c: File reception routines for xprzmodem.library;
* Version 2.10, 12 February 1991, by Rick Huebner.
* Based closely on Chuck Forsberg's rz.c example ZModem code,
* but too pervasively modified to even think of detailing the changes.
* Released to the Public Domain; do as you like with this code.
*
* Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
**********************************************************************/
#include <proto/all.h>
#include <exec/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xproto.h"
#include "zmodem.h"
#include "xprzmodem.h"
#ifdef DEBUGLOG
extern void *DebugLog;
#endif
/**********************************************************
* long XProtocolReceive(struct XPR_IO *xio)
*
* Main file reception routine; called by comm program
**********************************************************/
long __saveds __asm XProtocolReceive(register __a0 struct XPR_IO *xio)
{
struct SetupVars *sv;
struct Vars *v;
UBYTE err = FALSE;
/* Perform common setup and initializations */
if (! (v = setup(xio)))
return XPRS_FAILURE;
v->Tryzhdrtype = ZRINIT;
v->Rxtimeout = 100;
sv = (void *) v->io.xpr_data;
if (sv->bufpos)
{
v->Modemchar = v->Modembuf;
if (sv->buflen > sizeof(v->Modembuf))
sv->buflen = sizeof(v->Modembuf);
memcpy(v->Modembuf, sv->bufpos,sv->buflen);
v->Modemcount = sv->buflen;
}
/* Transfer the files */
if (rcvbatch(v) == ERROR)
{
upderr(v, "Download cancelled or timed out");
err = TRUE;
}
else
updmsg(v, "Done.");
/* Clean up and return */
if (v->io.xpr_setserial && v->Oldstatus != -1)
(*v->io.xpr_setserial)(v->Oldstatus);
FreeMem(v->Filebuf, v->Filebufmax);
FreeMem(v, (long) sizeof(struct Vars));
#ifdef DEBUGLOG
if (DebugLog)
{
xpr_fclose(&v->io, DebugLog);
DebugLog = NULL;
}
#endif
return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
} /* End of long XProtocolReceive() */
/**********************************************************
* short rcvbatch(struct Vars *v)
*
* Start the batch transfer
**********************************************************/
short rcvbatch(struct Vars *v)
{
switch (tryz(v))
{
case ZCOMPL:
return OK;
case ZFILE:
if (rzfiles(v) == OK)
return OK;
}
canit(v);
return ERROR;
} /* End of short rcvbatch() */
/**********************************************************
* short tryz(struct Vars *v)
*
* Negotiate with sender to start a file transfer
**********************************************************/
short tryz(struct Vars *v)
{
short n, errors = 0;
for (n = v->ErrorLimit; --n >= 0; )
{
/* Set max frame length and capability flags */
stohdr(v, (long) v->Tframlen);
v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
zshhdr(v, v->Tryzhdrtype);
sendbuf(v);
again:
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
return ERROR;
switch (zgethdr(v))
{
case ZFILE: /* File name and info packet */
v->Zconv = v->Rxhdr[ZF0]; /* Suggested txt mode; ZCNL = text, */
/* ZCBIN = binary, 0 = don't know. */
v->Zmanag = v->Rxhdr[ZF1]; /* Suggested file management mode. */
v->Ztrans = v->Rxhdr[ZF2]; /* Suggested file transport mode. */
v->Tryzhdrtype = ZRINIT;
if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW)
return ZFILE;
zshhdr(v, ZNAK); /* Packet mangled, ask for retry */
sendbuf(v);
goto again;
case ZSINIT: /* Special attention-grabbing string to use to */
/* interrupt sender */
if (zrdata(v, v->Attn, ZATTNLEN) == GOTCRCW)
zshhdr(v, ZACK);
else
zshhdr(v, ZNAK);
sendbuf(v);
goto again;
case ZFREECNT: /* Sender wants to know how much room we've got */
stohdr(v, getfree());
zshhdr(v, ZACK);
sendbuf(v);
goto again;
case ZCOMMAND: /* Sender wants us to do remote commands, */
/* but we don't do requests. */
if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW)
{
mysprintf(v->Msgbuf, "Ignoring command: %s", v->Pktbuf);
upderr(v, v->Msgbuf); /* Ignore and report all uploaded commands */
stohdr(v, 0L); /* whilst telling sender they worked; */
do
{
zshhdr(v, ZCOMPL); /* paranoia can be good for you... */
sendbuf(v);
}
while (++errors < v->ErrorLimit && zgethdr(v) != ZFIN);
ackbibi(v);
return ZCOMPL;
}
else
zshhdr(v, ZNAK);
sendbuf(v);
goto again;
case ZCOMPL:
goto again;
case ZFIN: /* Sender has ended batch */
ackbibi(v);
return ZCOMPL;
case ZCAN:
case RCDO:
upderr(v, v->Msgbuf);
return ERROR;
}
}
return ERROR;
} /* End of short tryz() */
/**********************************************************
* short rzfiles(struct Vars *v)
*
* Receive a batch of files
**********************************************************/
short rzfiles(struct Vars *v)
{
struct SetupVars *sv;
short c;
/* Keep receiving files until end of batch or error */
while (TRUE)
{
switch (c = rzfile(v))
{
case ZEOF:
case ZSKIP:
switch (tryz(v))
{
case ZCOMPL:
return OK;
default:
return ERROR;
case ZFILE:
break;
}
break;
default:
bfclose(v);
sv = (void *) v->io.xpr_data;
if (*sv->option_k == 'N' && v->io.xpr_extension >= 2
&& v->io.xpr_unlink)
{
updmsg(v, "Deleting partially received file");
(*v->io.xpr_unlink)(v->Filename);
}
else
updmsg(v, "Keeping partially received file");
return c;
}
}
} /* End of short rzfiles() */
/**********************************************************
* short rzfile(struct Vars *v)
*
* Receive one file; file name packet already read into
* Pktbuf by tryz()
**********************************************************/
short rzfile(struct Vars *v)
{
short c, n;
/*
* Process file name packet; either open file and prepare to receive,
* or tell us to skip this one.
*/
if (procheader(v) == ERROR)
return v->Tryzhdrtype = ZSKIP;
n = v->ErrorLimit;
v->Rxbytes = v->Strtpos;
v->Eofseen = FALSE;
/* Receive ZDATA frames until finished */
while (TRUE)
{
stohdr(v, v->Rxbytes); /* Tell sender where to start frame */
zshhdr(v, ZRPOS);
sendbuf(v);
nxthdr:
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
return ERROR;
switch (c = zgethdr(v)) /* Wait for frame header */
{
default:
#ifdef DEBUGLOG
mysprintf(v->Msgbuf, "rzfile: zgethdr returned %ld\n", (long) c);
dlog(v, v->Msgbuf);
#endif
return ERROR;
case ZNAK:
case TIMEOUT:
if (--n < 0)
return ERROR;
#ifdef DEBUGLOG
dlog(v, "rzfile: zgethdr NAK/Timeout\n");
#endif
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_timeouts;
(*v->io.xpr_update)(&v->xpru);
continue;
case ZFILE: /* Sender didn't see our ZRPOS yet; try again */
zrdata(v, v->Pktbuf, KSIZE); /* Read and discard redundant */
continue; /* filename packet */
case ZEOF: /* End of file data */
if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
{
mysprintf(v->Msgbuf, "Bad EOF; here=%ld, there=%ld",
v->Rxbytes, v->Rxpos);
upderr(v, v->Msgbuf);
continue;
}
bfclose(v); /* All done; close file */
#ifdef DEBUGLOG
dlog(v, "rzfile: EOF\n");
#endif
updmsg(v, "EOF received; checking for next file");
return c;
case ERROR: /* Too much garbage while waiting for frame header */
if ( --n < 0)
return ERROR;
#ifdef DEBUGLOG
dlog(v, "rzfile: zgethdr garbage overflow\n");
#endif
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
zmputs(v, v->Attn);
continue;
case ZDATA: /* More file data packets forthcoming */
if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
{
if ( --n < 0)
return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(v->Msgbuf, "Data at bad position; here=%ld, there=%ld",
v->Rxbytes, v->Rxpos);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
zmputs(v, v->Attn);
continue;
}
/* Receive file data packet(s) */
moredata:
/* Give comm program its timeslice if it needs one */
if (v->io.xpr_chkmisc)
(*v->io.xpr_chkmisc)();
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
goto aborted;
switch (c = zrdata(v, v->Pktbuf,KSIZE))
{
case ZCAN:
case RCDO:
aborted:
#ifdef DEBUGLOG
dlog(v, "rzfile: zrdata returned CAN\n");
#endif
upderr(v, "Transfer cancelled");
return ERROR;
case ERROR: /* CRC error or packet too long */
if ( --n < 0)
return ERROR;
#ifdef DEBUGLOG
dlog(v, "rzfile: zrdata returned error\n");
#endif
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
#ifdef DEBUGLOG
dlog(v, v->Msgbuf);
dlog(v, "\n");
#endif
zmputs(v, v->Attn);
continue;
case TIMEOUT:
if ( --n < 0)
return ERROR;
#ifdef DEBUGLOG
dlog(v, "rzfile: zrdata returned timeout\n");
#endif
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_timeouts;
(*v->io.xpr_update)(&v->xpru);
#ifdef DEBUGLOG
dlog(v, v->Msgbuf);
dlog(v, "\n");
#endif
continue;
case GOTCRCW: /* Sender says it's waiting for an ACK */
n = v->ErrorLimit;
if (putsec(v) == ERROR)
return ERROR;
stohdr(v, v->Rxbytes);
zshhdr(v, ZACK);
sendbuf(v);
goto nxthdr;
case GOTCRCQ: /* Sender says it's not waiting, */
/* but ACK anyway (rarely used) */
n = v->ErrorLimit;
if (putsec(v) == ERROR)
return ERROR;
stohdr(v, v->Rxbytes);
zshhdr(v, ZACK);
sendbuf(v);
goto moredata;
case GOTCRCG: /* Sender says keep receiving, there's more coming */
n = v->ErrorLimit;
if (putsec(v) == ERROR)
return ERROR;
goto moredata;
case GOTCRCE: /* Sender says this is the last packet */
n = v->ErrorLimit;
if (putsec(v) == ERROR)
return ERROR;
goto nxthdr;
}
}
}
} /* End of short rzfile() */
/**********************************************************
* short procheader(struct Vars *v)
*
* Process file name & info packet; either open file and
* prepare to receive, or return ERROR if we should skip
* this one for some reason
**********************************************************/
short procheader(struct Vars *v)
{
struct SetupVars *sv;
UBYTE *p, *openmode, buff[PATHLEN];
long n;
openmode = "w";
v->Strtpos = 0;
/* Extract expected filesize from file info packet, if given */
v->Fsize = -1;
p = strchr(v->Pktbuf, '\0') + 1;
if (*p)
v->Fsize = atol(p);
/*
* Make sure we have room for file; skip it if not.
* Commented out for now, since getfree() isn't implemented yet.
if (v->Fsize > getfree())
{
mysprintf(v->Msgbuf, "Insufficient disk space; need %ld bytes, have %ld",
v->Fsize, getfree());
upderr(v, v->Msgbuf);
v->Noroom = TRUE;
return ERROR;
}
*/
/* If option RY set, use full received file path */
sv = (void *) v->io.xpr_data;
if (*sv->option_r == 'Y')
strcpy(v->Filename, v->Pktbuf);
else
{
/* else use the default directory path specified in the setup options */
strcpy(v->Filename, sv->option_p);
p = v->Filename + strlen(v->Filename) - 1;
if (p >= v->Filename && *p != '/' && *p != ':')
*++p = '/';
*++p = '\0';
/*
* Append the filename from the file info packet; ignore anything before
* last /, \, or : in filename (received directory path is ignored)
*/
p = strchr(v->Pktbuf, '\0'); /* start at end and scan back */
/* to start of name */
while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':')
--p;
strcat(v->Filename, ++p);
}
/* Display name of file being received for user */
v->xpru.xpru_updatemask = XPRU_FILENAME;
v->xpru.xpru_filename = (char *) v->Filename;
(*v->io.xpr_update)(&v->xpru);
/*
* If a file with this name already exists, handle in
* accordance with O option
*/
if (exist(v))
{
switch (*sv->option_o)
{
case 'N': /* Don't overwrite; change name to prevent collision */
strcpy(buff, v->Filename);
strcat(v->Filename, ".dup");
n = 2;
while (exist(v))
{
mysprintf(v->Filename, "%s.dup%ld", buff, n);
++n;
}
/* Update filename display to show new name */
(*v->io.xpr_update)(&v->xpru);
break;
case 'R': /* Resume transfer from current end of file */
openmode = "a";
v->Strtpos = (*v->io.xpr_finfo)(v->Filename, 1L);
break;
case 'S': /* Skip it */
upderr(v, "File already exists; skipping");
return ERROR;
/* Else 'Y', go ahead and overwrite it (openmode = w) */
}
}
/* Set text/binary mode according to options before opening file */
set_textmode(v);
/*
* Figure out file translation mode to use; either binary (verbatim
* transfer) or ASCII (perform end-of-line conversions). If user has
* specified a mode (TY or TN), that's what we use. If user says use
* sender's suggestion (T?), set mode according to Zconv flag. If neither
* side specifies, default to binary mode.
*/
v->Thisbinary = v->Rxbinary || !v->Rxascii;
if (! v->Rxbinary && v->Zconv == ZCNL)
v->Thisbinary = FALSE;
if (! v->Rxascii && v->Zconv == ZCBIN)
v->Thisbinary = TRUE;
/* Open the file (finally) */
if (! (v->File = bfopen(v, openmode)))
{
++v->Errcnt;
upderr(v, "Can't open file; skipping");
return ERROR;
}
getsystime(&v->Starttime);
/* Initialize comm program transfer status display */
v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
| XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
| XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME| XPRU_DATARATE;
v->xpru.xpru_protocol = "ZModem";
v->xpru.xpru_filesize = v->Fsize;
v->xpru.xpru_msg = (v->Thisbinary) ? "Receiving binary file..."
: "Receiving text file...";
v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
v->xpru.xpru_bytes = v->Strtpos;
update_rate(v);
(*v->io.xpr_update)(&v->xpru);
return OK;
} /* End of short procheader() */
/**********************************************************
* short putsec(struct Vars *v)
*
* Writes the received file data to the output file.
* If in ASCII mode, stops writing at first ^Z, and converts all
* \r\n pairs or solo \r's to \n's.
**********************************************************/
short putsec(struct Vars *v)
{
static char nl = '\n';
UBYTE *p;
short n;
/* If in binary mode, write it out verbatim */
if (v->Thisbinary)
{
if (bfwrite(v, v->Pktbuf, (long) v->Rxcount) != v->Rxcount)
goto diskfull;
/* If in text mode, perform end-of-line cleanup */
}
else
{
if (v->Eofseen)
return OK;
for (p = v->Pktbuf, n = v->Rxcount; --n >= 0; ++p)
{
if (*p == CPMEOF)
{
v->Eofseen = TRUE;
return OK;
}
else if (*p != '\n' && v->Lastsent == '\r')
{
if (bfwrite(v, &nl, 1L) != 1)
goto diskfull;
}
if (*p != '\r' && bfwrite(v, p, 1L) != 1)
goto diskfull;
v->Lastsent = *p;
}
}
/* Update comm program status display */
v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
| XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKCHECK;
++v->xpru.xpru_blocks;
v->xpru.xpru_blocksize = v->Rxcount;
v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
update_rate(v);
(*v->io.xpr_update)(&v->xpru);
return OK;
diskfull:
upderr(v, "Error writing file; disk full?");
v->Noroom = TRUE;
return ERROR;
} /* End of short putsec() */
/**********************************************************
* void ackbibi(struct Vars *v)
*
* End of batch transmission; disengage cleanly from sender
**********************************************************/
void ackbibi(struct Vars *v)
{
short n;
#ifdef DEBUGLOG
dlog(v, "ackbibi:\n");
#endif
stohdr(v, 0L);
for (n = 4; --n; )
{
zshhdr(v, ZFIN);
sendbuf(v);
switch (readock(v, 100))
{
case 'O':
readock(v, 1); /* Discard 2nd 'O' */
case TIMEOUT:
case RCDO:
return;
}
}
} /* End of void ackbibi() */
/* End of Receive.c source */